﻿Imports System.ComponentModel
Imports LSW.Win32
Imports System.Drawing.Imaging
Imports System.Drawing.Drawing2D
Imports System.Drawing.Text

Namespace Forms
    Public Class FormBase
        Inherits Form
#Region "变量"
        ''' <summary>
        ''' 系统按钮
        ''' </summary>
        Protected _sysButton As ESysButton = ESysButton.Normal
        ''' <summary>
        ''' 关闭按钮的鼠标状态
        ''' </summary>
        Protected _closeState As EMouseState = EMouseState.Normal
        ''' <summary>
        ''' 最大化按钮的鼠标状态
        ''' </summary>
        Protected _maxState As EMouseState = EMouseState.Normal
        ''' <summary>
        ''' 最小化按钮的鼠标状态
        ''' </summary>
        Protected _minState As EMouseState = EMouseState.Normal
        ''' <summary>
        ''' 记录窗体大小
        ''' </summary>
        Protected _formSize As Size = Size.Empty
        ''' <summary>
        ''' 记录窗体位置
        ''' </summary>
        Protected _formPoint As Point = Point.Empty
        ''' <summary>
        ''' 指定窗体窗口如何显示
        ''' </summary>
        Protected _windowState As FormWindowState = FormWindowState.Normal
        ''' <summary>
        ''' 是否允许改变窗口大小
        ''' </summary>
        Protected _isResize As Boolean = True
        ''' <summary>
        ''' 是否显示图标
        ''' </summary>
        Protected _showIcon As Boolean = True
        ''' <summary>
        ''' 是否透明化
        ''' </summary>
        Private _isTransfer As Boolean = False
#End Region

#Region "构造函数"
        ''' <summary>
        ''' 初始化 Paway.Windows.Forms.FormBase 类的新实例。
        ''' </summary>
        Public Sub New()
            Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.Selectable Or ControlStyles.ContainerControl Or ControlStyles.UserPaint, True)
            Me.SetStyle(ControlStyles.Opaque, False)
            Me.UpdateStyles()
            Me.Initialize()
        End Sub
#End Region

#Region "属性"
        ''' <summary>
        ''' 是否允许改变窗口大小
        ''' </summary>
        <Description("是否允许改变窗口大小"), DefaultValue(True)> _
        Public Overridable Property IsResize() As Boolean
            Get
                Return Me._isResize
            End Get
            Set(value As Boolean)
                _isResize = value
            End Set
        End Property
        ''' <summary>
        ''' 指定窗体窗口如何显示
        ''' </summary>
        <Description("指定窗体窗口如何显示")> _
        Public Overridable Shadows Property WindowState() As FormWindowState
            Get
                Return Me._windowState
            End Get
            Set(value As FormWindowState)
                Me._windowState = value
                Select Case Me._windowState
                    Case FormWindowState.Normal
                        MyBase.WindowState = FormWindowState.Normal
                        Exit Select
                    Case FormWindowState.Minimized
                        MyBase.WindowState = FormWindowState.Minimized
                        Exit Select
                    Case FormWindowState.Maximized
                        Me._formSize = Me.Size
                        Me._formPoint = Me.Location
                        Me.Location = New Point(0, 0)
                        Me.Width = Screen.PrimaryScreen.WorkingArea.Width
                        Me.Height = Screen.PrimaryScreen.WorkingArea.Height
                        Exit Select
                End Select
            End Set
        End Property
        ''' <summary>
        ''' 窗体大小的最小值
        ''' </summary>
        Public Overrides Property MinimumSize() As Size
            Get
                Return New Size(140, 40)
            End Get
            Set(value As Size)
                MyBase.MinimumSize = value
            End Set
        End Property
        ''' <summary>
        ''' 是否透明
        ''' </summary>
        Public Property IsTransfer() As Boolean
            Get
                Return Me._isTransfer
            End Get
            Set(value As Boolean)
                Me._isTransfer = value
            End Set
        End Property
        ''' <summary>
        ''' 封装创建控件时所需的信息。
        ''' </summary>
        Protected Overrides ReadOnly Property CreateParams() As CreateParams
            Get
                If Not Me._isTransfer Then
                    Return MyBase.CreateParams
                Else
                    Dim param As CreateParams = MyBase.CreateParams
                    param.ExStyle = &H80000
                    Return param
                End If
            End Get
        End Property
        ''' <summary>
        ''' 是否显示图标
        ''' </summary>
        Public Overridable Shadows Property ShowIcon() As Boolean
            Get
                Return Me._showIcon
            End Get
            Set(value As Boolean)
                Me._showIcon = value
                MyBase.Invalidate(Me.TitleBarRect)
            End Set
        End Property
        ''' <summary>
        ''' 窗体标题文字
        ''' </summary>
        Public Overrides Property Text() As String
            Get
                Return MyBase.Text
            End Get
            Set(value As String)
                MyBase.Text = value
                MyBase.Invalidate(Me.TitleBarRect)
            End Set
        End Property

        ''' <summary>
        ''' 系统控制按钮
        ''' </summary>
        <Description("系统控制按钮的显示与隐藏")> _
        Public Property SysButton() As ESysButton
            Get
                Return Me._sysButton
            End Get
            Set(value As ESysButton)
                Me._sysButton = value
                MyBase.Invalidate(Me.TitleBarRect)
            End Set
        End Property
        ''' <summary>
        ''' 系统控制按钮区域
        ''' </summary>
        Protected Overridable ReadOnly Property SysBtnRect() As Rectangle
            Get
                Return Rectangle.Empty
            End Get
        End Property
        ''' <summary>
        ''' 标题栏区域
        ''' </summary>
        Protected Overridable ReadOnly Property TitleBarRect() As Rectangle
            Get
                Return New Rectangle(0, 0, Me.Width, 30)
            End Get
        End Property
        ''' <summary>
        ''' 关闭按钮区域
        ''' </summary>
        Protected Overridable ReadOnly Property CloseRect() As Rectangle
            Get
                Return Rectangle.Empty
            End Get
        End Property
        ''' <summary>
        ''' 最小化按钮区域
        ''' </summary>
        Protected Overridable ReadOnly Property MiniRect() As Rectangle
            Get
                Return Rectangle.Empty
            End Get
        End Property
        ''' <summary>
        ''' 最大化按钮区域
        ''' </summary>
        Protected Overridable ReadOnly Property MaxRect() As Rectangle
            Get
                Return Rectangle.Empty
            End Get
        End Property
        ''' <summary>
        ''' 图标显示区域
        ''' </summary>
        Protected Overridable ReadOnly Property IconRect() As Rectangle
            Get
                Return New Rectangle(4, 4, 16, 16)
            End Get
        End Property
        ''' <summary>
        ''' 标题文本显示区域
        ''' </summary>
        Protected Overridable ReadOnly Property TextRect() As Rectangle
            Get
                Dim width As Integer = Me.TitleBarRect.Width - Me.IconRect.Width - 15
                Dim height As Integer = Me.TitleBarRect.Height - 10
                Dim textRect__1 As New Rectangle(8, 2, width, height)
                If Me.ShowIcon Then
                    textRect__1.X = Me.IconRect.Width + 8
                End If
                Return textRect__1
            End Get
        End Property
        ''' <summary>
        ''' 关闭按钮当前的鼠标状态
        ''' </summary>
        Protected Property CloseState() As EMouseState
            Get
                Return Me._closeState
            End Get
            Set(value As EMouseState)
                Me._closeState = value
                MyBase.Invalidate(Me.CloseRect)
            End Set
        End Property
        ''' <summary>
        ''' 最大化按钮当前的鼠标状态
        ''' </summary>
        Protected Property MaxState() As EMouseState
            Get
                Return Me._maxState
            End Get
            Set(value As EMouseState)
                Me._maxState = value
                MyBase.Invalidate(Me.MaxRect)
            End Set
        End Property
        ''' <summary>
        ''' 最小化按钮当前的鼠标状态
        ''' </summary>
        Protected Property MinState() As EMouseState
            Get
                Return Me._minState
            End Get
            Set(value As EMouseState)
                Me._minState = value
                MyBase.Invalidate(Me.MiniRect)
            End Set
        End Property
#End Region

#Region "方法"

        ''' <summary>
        ''' 初始化窗口
        ''' </summary>
        Private Sub Initialize()
            Me.FormBorderStyle = FormBorderStyle.None
        End Sub

        ''' <summary>
        ''' 拖动窗口大小
        ''' </summary>
        ''' <param name="m"></param>
        Private Sub WmNcHitTest(ByRef m As Message)
            If Me.WindowState <> FormWindowState.Maximized Then
                Dim wparam As Integer = m.LParam.ToInt32()

                Dim point As New Point(NativeMethods.LOWORD(wparam), NativeMethods.HIWORD(wparam))

                point = PointToClient(point)
                If _isResize Then
                    If point.X <= 3 Then
                        If point.Y <= 3 Then
                            m.Result = CType(WM_NCHITTEST.HTTOPLEFT, IntPtr)
                        ElseIf point.Y > Height - 3 Then
                            m.Result = CType(WM_NCHITTEST.HTBOTTOMLEFT, IntPtr)
                        Else
                            m.Result = CType(WM_NCHITTEST.HTLEFT, IntPtr)
                        End If
                    ElseIf point.X >= Width - 3 Then
                        If point.Y <= 3 Then
                            m.Result = CType(WM_NCHITTEST.HTTOPRIGHT, IntPtr)
                        ElseIf point.Y >= Height - 3 Then
                            m.Result = CType(WM_NCHITTEST.HTBOTTOMRIGHT, IntPtr)
                        Else
                            m.Result = CType(WM_NCHITTEST.HTRIGHT, IntPtr)
                        End If
                    ElseIf point.Y <= 3 Then
                        m.Result = CType(WM_NCHITTEST.HTTOP, IntPtr)
                    ElseIf point.Y >= Height - 3 Then
                        m.Result = CType(WM_NCHITTEST.HTBOTTOM, IntPtr)
                    Else
                        MyBase.WndProc(m)
                    End If
                Else
                    MyBase.WndProc(m)
                End If
            End If
        End Sub

        ''' <summary>
        ''' 设置图片为窗体，透明区域根据 opacity 的值决定透明度
        ''' </summary>
        ''' <param name="bitmap">透明位图</param>
        ''' <param name="opacity">透明度的值0~255</param>
        Public Sub SetBitmap(bitmap As Bitmap, opacity As Byte)
            If bitmap.PixelFormat <> PixelFormat.Format32bppArgb Then
                Throw New ApplicationException("The bitmap must be 32ppp with alpha-channel.")
            End If

            ' The ideia of this is very simple,
            ' 1. Create a compatible DC with screen;
            ' 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC;
            ' 3. Call the UpdateLayeredWindow.

            Dim screenDc As IntPtr = User32.GetDC(IntPtr.Zero)
            Dim memDc As IntPtr = Gdi32.CreateCompatibleDC(screenDc)
            Dim hBitmap As IntPtr = IntPtr.Zero
            Dim oldBitmap As IntPtr = IntPtr.Zero

            Try
                hBitmap = bitmap.GetHbitmap(Color.FromArgb(0))
                ' grab a GDI handle from this GDI+ bitmap
                oldBitmap = Gdi32.SelectObject(memDc, hBitmap)

                Dim size As New Size(bitmap.Width, bitmap.Height)
                Dim pointSource As New Point(0, 0)
                Dim topPos As New Point(Left, Top)
                Dim blend As New BLENDFUNCTION()
                blend.BlendOp = Consts.AC_SRC_OVER
                blend.BlendFlags = 0
                blend.SourceConstantAlpha = opacity
                blend.AlphaFormat = Consts.AC_SRC_ALPHA

                User32.UpdateLayeredWindow(Handle, screenDc, topPos, size, memDc, pointSource, 0, blend, Consts.ULW_ALPHA)
            Finally
                User32.ReleaseDC(IntPtr.Zero, screenDc)
                If hBitmap <> IntPtr.Zero Then
                    Gdi32.SelectObject(memDc, oldBitmap)
                    'Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak.
                    Gdi32.DeleteObject(hBitmap)
                End If
                Gdi32.DeleteDC(memDc)
            End Try
        End Sub

#End Region

#Region "Override Methods"
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.Paint 事件。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.Windows.Forms.PaintEventArgs。</param>
        Protected Overrides Sub OnPaint(e As PaintEventArgs)
            MyBase.OnPaint(e)
            Dim g As Graphics = e.Graphics
            g.SmoothingMode = SmoothingMode.AntiAlias
            g.TextRenderingHint = TextRenderingHint.AntiAlias

            If Me.ShowIcon Then
                '绘画图标
                Dim iconImage As Bitmap = Me.Icon.ToBitmap()
                g.DrawImage(iconImage, Me.IconRect)
            End If
            '绘制标题文字
            If Not String.IsNullOrEmpty(Me.Text) Then
                TextRenderer.DrawText(g, Me.Text, New Font("宋体", 9.0F, FontStyle.Bold), Me.TextRect, Me.ForeColor, TextFormatFlags.VerticalCenter)
            End If
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.Load 事件。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.EventArgs。</param>
        Protected Overrides Sub OnLoad(e As EventArgs)
            MyBase.OnLoad(e)
            If Not Me.DesignMode Then
                Select Case Me.StartPosition
                    Case FormStartPosition.CenterParent
                        Me.Location = New Point((Me.Parent.Width - Me.Width) / 2, (Me.Parent.Height - Me.Height) / 2)
                        Exit Select
                    Case FormStartPosition.CenterScreen
                        Me.Location = New Point((Screen.PrimaryScreen.WorkingArea.Width - Me.Width) / 2, (Screen.PrimaryScreen.WorkingArea.Height - Me.Height) / 2)
                        Exit Select
                    Case FormStartPosition.Manual, FormStartPosition.WindowsDefaultBounds, FormStartPosition.WindowsDefaultLocation
                        Exit Select
                End Select
            End If
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.SizeChanged。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.EventArgs。</param>
        Protected Overrides Sub OnSizeChanged(e As EventArgs)
            MyBase.OnSizeChanged(e)
            MyBase.Padding = New Padding(3, 26, 3, 3)
        End Sub
        ''' <summary>
        ''' 处理 Windows 消息。
        ''' </summary>
        ''' <param name="m">要处理的 WindowsMessage。</param>
        Protected Overrides Sub WndProc(ByRef m As Message)
            Try
                Select Case m.Msg
                    Case CInt(WindowsMessage.WM_NCPAINT), CInt(WindowsMessage.WM_NCCALCSIZE)
                        Exit Select
                    Case CInt(WindowsMessage.WM_NCACTIVATE)
                        If m.WParam = CType(0, IntPtr) Then
                            m.Result = CType(1, IntPtr)
                        End If
                        Exit Select
                    Case CInt(WindowsMessage.WM_NCHITTEST)
                        MyBase.WndProc(m)
                        Me.WmNcHitTest(m)
                        Exit Select
                    Case Else
                        MyBase.WndProc(m)
                        Exit Select
                End Select
            Catch
            End Try
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.MouseDown。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.Windows.Forms.MouseEventArgs。</param>
        Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
            MyBase.OnMouseDown(e)
            Dim point As Point = e.Location
            If e.Button = MouseButtons.Left Then
                If Me.CloseRect.Contains(point) Then
                    Me.CloseState = EMouseState.Down
                ElseIf Me.MiniRect.Contains(point) Then
                    Me.MinState = EMouseState.Down
                ElseIf Me.MaxRect.Contains(point) Then
                    Me.MaxState = EMouseState.Down
                End If
            End If
            If Me.WindowState <> FormWindowState.Maximized Then
                If e.Button = MouseButtons.Left AndAlso Not Me.SysBtnRect.Contains(e.Location) Then
                    User32.ReleaseCapture()
                    User32.SendMessage(Handle, 274, 61440 + 9, 0)
                End If
            End If
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.MouseMove。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.Windows.Forms.MouseEventArgs。</param>
        Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
            MyBase.OnMouseMove(e)
            If e.Button = MouseButtons.Left Then
                Return
            End If
            Dim point As Point = e.Location
            If Me.CloseRect.Contains(point) Then
                Me.CloseState = EMouseState.Move
            Else
                Me.CloseState = EMouseState.Normal
            End If
            If Me.MiniRect.Contains(point) Then
                Me.MinState = EMouseState.Move
            Else
                Me.MinState = EMouseState.Normal
            End If
            If Me.SysButton = ESysButton.Normal Then
                If Me.MaxRect.Contains(point) Then
                    Me.MaxState = EMouseState.Move
                Else
                    Me.MaxState = EMouseState.Normal
                End If
            End If
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.MouseLeave。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.EventArgs。</param>
        Protected Overrides Sub OnMouseLeave(e As EventArgs)
            MyBase.OnMouseLeave(e)
            Me.CloseState = EMouseState.Normal
            Me.MaxState = EMouseState.Normal
            Me.MinState = EMouseState.Normal
        End Sub
        ''' <summary>
        ''' 引发 System.Windows.Forms.Form.MouseUp。
        ''' </summary>
        ''' <param name="e">包含事件数据的 System.Windows.Forms.MouseEventArgs。</param>
        Protected Overrides Sub OnMouseUp(e As MouseEventArgs)
            MyBase.OnMouseUp(e)
            If e.Button <> MouseButtons.Left Then
                Return
            End If
            Dim point As Point = e.Location
            If Me.CloseRect.Contains(point) Then
                Me.CloseState = EMouseState.Move
                Me.Close()
            Else
                Me.CloseState = EMouseState.Normal
            End If
            If Me.MiniRect.Contains(point) Then
                Me.MinState = EMouseState.Move
                Me.WindowState = FormWindowState.Minimized
            Else
                Me.MinState = EMouseState.Normal
            End If
            If Me.MaxRect.Contains(point) Then
                Me.MaxState = EMouseState.Move
                If Me.WindowState = FormWindowState.Maximized Then
                    Me.Size = Me._formSize
                    Me.Location = Me._formPoint
                    Me.WindowState = FormWindowState.Normal
                Else
                    Me.WindowState = FormWindowState.Maximized
                End If
            Else
                Me.MaxState = EMouseState.Normal
            End If
        End Sub
#End Region
    End Class
End Namespace